package com.myzl.coal.client.parse;

import com.alibaba.fastjson2.JSONArray;
import com.google.common.collect.Lists;
import com.google.common.primitives.Bytes;
import com.myzl.coal.client.CacheData;
import com.myzl.coal.client.config.LocalCommunicationParse;
import com.myzl.coal.domain.dto.RealTimeDataDTO;
import com.myzl.coal.mongo.component.MongoComponent;
import com.myzl.coal.mq.MqService;
import com.myzl.coal.service.IRealTimeDataService;
import com.myzl.coal.service.impl.DemoService;
import com.myzl.common.constant.DeviceConstants;
import com.myzl.common.core.domain.entity.SysDictData;
import com.myzl.common.enums.DemoEnum;
import com.myzl.common.enums.DeviceCommandEnum;
import com.myzl.common.enums.DeviceTypeEnum;
import com.myzl.common.enums.SieveRadarCommandEnum;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static com.myzl.coal.utils.HexUtil.*;

/**
 * @author 91941
 */
@Component
@Slf4j(topic = "admin-collect")
@LocalCommunicationParse(deviceType = DeviceConstants.LIDAR)
@Data
public class OuLeiLidarParse extends CommonParseService {

    Map<Integer, Integer> countMap = new ConcurrentHashMap<>(16);
    Map<Integer, byte[]> rangesMap = new ConcurrentHashMap<>(16);

    private final IRealTimeDataService realTimeDataService;
    private final CacheData cacheData;
    private final MqService mqService;
    private final MongoComponent mongoComponent;
    private final DemoService demoService;

    /**
     * 客户端发消息会触发
     */
    @Override
    public void channelRead(Object msg, Integer deviceId) {
        try {

            byte[] dataBytes = (byte[]) msg;
            String dataStr = bytesToHexString(dataBytes);
//            log.debug("{} receive Data:{}",this.getClass().getSimpleName(), dataStr);
            if (dataBytes.length < 42) {
                log.info("{} length < 42 receive Data:{}", this.getClass().getSimpleName(), dataStr);
                return;
            }
            int block = BigDecimal.valueOf(toInt(reverse(getBytes(dataBytes, 40, 2)))).divide(BigDecimal.valueOf(100), 0, RoundingMode.HALF_UP).divide(BigDecimal.valueOf(33.75), 0, RoundingMode.HALF_UP).intValue();
            byte[] ranges = getBytes(dataBytes, 40, 8 * 150);
            if (timeMap.containsKey(deviceId)) {
                LocalDateTime dateTime = timeMap.get(deviceId);
                if (dateTime.plusSeconds(1).isAfter(LocalDateTime.now())) {
                    return;
                }
            }
            Integer blockCount = countMap.getOrDefault(deviceId, 0);
            if (!Objects.equals(blockCount, block)) {
                countMap.put(deviceId, 0);
                rangesMap.put(deviceId, new byte[0]);
                return;
            }

            countMap.put(deviceId, ++blockCount);
            byte[] totalBytes = rangesMap.getOrDefault(deviceId, new byte[0]);
            byte[] concat = Bytes.concat(totalBytes, ranges);
            rangesMap.put(deviceId, concat);

            if (blockCount < 11) {
                return;
            }

            /**获取拼接data解析*/
            byte[] bytes = rangesMap.get(deviceId);
            log.info("{} receive Data:{}", this.getClass().getSimpleName(), bytesToHexString(bytes));
            /**获取初始数据*/
            double offset = 2;
            double totalH = cacheData.getRealTimeData(deviceId, DeviceCommandEnum.HIGH.getCode()) + offset;
            double halfX = BigDecimal.valueOf(cacheData.getRealTimeData(deviceId, DeviceCommandEnum.WIDTH.getCode())).divide(BigDecimal.valueOf(2), 2, RoundingMode.HALF_UP).doubleValue();

            double realX, realY, x, y;
            List<Double> vLists = new ArrayList<>();
            List<Double> xLists = new ArrayList<>();
            List<Double> yLists = new ArrayList<>();
            for (int i = 0, j = 0; i < bytes.length; i += 8, j++) {
                int k = i;
                double degrees = BigDecimal.valueOf(toInt(reverse(getBytes(bytes, k, 2)))).divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP).doubleValue();

                double distance = toInt(reverse(getBytes(bytes, k + 2, 2))) / 10;
                int signal = toInt(reverse(getBytes(bytes, k + 4, 2)));
                int block4 = toInt(reverse(getBytes(bytes, k + 6, 2)));


                if (distance != 0) {
                    double radians = Math.toRadians(degrees);
                    double sin = Math.sin(radians);
                    double cos = Math.cos(radians);

                    x = BigDecimal.valueOf(distance).multiply(BigDecimal.valueOf(sin)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                    y = BigDecimal.valueOf(distance).multiply(BigDecimal.valueOf(cos)).setScale(2, RoundingMode.HALF_UP).doubleValue();
                    if(cacheData.getRealTimeData(deviceId, SieveRadarCommandEnum.IS_ASC.getCode())==0){
                        x = -x;
                    }

                    if (Math.abs(x) < halfX && degrees > 90 && degrees < 270) {
                        realX = BigDecimal.valueOf(x).add(BigDecimal.valueOf(halfX)).setScale(1, RoundingMode.HALF_UP).doubleValue();
                        realY = BigDecimal.valueOf(totalH).add(BigDecimal.valueOf(y)).setScale(1, RoundingMode.HALF_UP).doubleValue();
                        //y不出现负数
                        realY = realY > 0 ? realY : 0.0;
                        log.debug("point:{},角度:{},距离读数:{}cm,x:{}cm,y:{}cm,realX:{}cm,realY:{}cm", j, degrees, distance, x, y, realX, realY);

                        vLists.add(distance);
                        xLists.add(realX);
                        yLists.add(realY);
                    } else {
                        log.debug("---point:{},角度:{},距离读数:{}cm,x:{}cm,y:{}cm,realX:{}cm,realY:{}cm", j, degrees, distance, x, y, 0, 0);
                    }
                }

            }
            if (!CollectionUtils.isEmpty(vLists)) {
                if(cacheData.getRealTimeData(deviceId,SieveRadarCommandEnum.IS_ASC.getCode())==0){
                    storage(deviceId, vLists, xLists, yLists);
                }else {
                    storage(deviceId, Lists.reverse(vLists), Lists.reverse(xLists), Lists.reverse(yLists));
                }
                storageData(mqService,DeviceTypeEnum.LIDAR);
                timeMap.put(deviceId, LocalDateTime.now());
            }
            rangesMap.clear();

            vLists.clear();
            xLists.clear();
            yLists.clear();
        } catch (Exception e) {
            log.info("receive data error:", e);
        }
    }


    public void storage(Integer deviceId, List<Double> vLists, List<Double> xLists, List<Double> yLists) {

        int size = yLists.size();
        int intValue = BigDecimal.valueOf(size + 1).multiply(BigDecimal.valueOf(0.3)).setScale(0, RoundingMode.HALF_UP).intValue();

        for (int i = 1; i < size - 1; i++) {
            double currentY = yLists.get(i);
            if (demoService.getRedisPoliceFlag(DemoEnum.coalHigh)) {
                yLists.set(i, currentY + 50);
            }else if (demoService.getRedisPoliceFlag(DemoEnum.coalDistribution)) {
                if (i < intValue) {
                    yLists.set(i, currentY + 30);
                }
            }
        }

//        for (int i = 1; i < yLists.size() - 1; i++) {
//
//            double currentY = yLists.get(i);
//            double currentX = xLists.get(i);
//
//            setEle(yLists, i, currentY);
//            setEle(xLists, i, currentX);
//        }


        List<Double> left = yLists.subList(0, intValue);
        List<Double> middle = yLists.subList(intValue, size - intValue);
        List<Double> right = yLists.subList(size - intValue, size);

        double totalAvg = yLists.stream().mapToDouble(Double::valueOf).average().getAsDouble();
        double leftAvg = left.stream().mapToDouble(Double::valueOf).average().getAsDouble();
        double middleAvg = middle.stream().mapToDouble(Double::valueOf).average().getAsDouble();
        double rightAvg = right.stream().mapToDouble(Double::valueOf).average().getAsDouble();

        List<String> storage = new ArrayList<>();
        storage.add(new JSONArray(vLists).toString());
        storage.add(new JSONArray(xLists).toString());
        storage.add(new JSONArray(yLists).toString());

        storage.add(Double.toString(BigDecimal.valueOf(totalAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));
        storage.add(Double.toString(BigDecimal.valueOf(leftAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));
        storage.add(Double.toString(BigDecimal.valueOf(middleAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));
        storage.add(Double.toString(BigDecimal.valueOf(rightAvg).setScale(2, RoundingMode.HALF_UP).doubleValue()));

        for (int i = 0; i < storage.size(); i++) {
            Integer type = 6000 + i;
            Map<String, SysDictData> dictMap = CacheData.getStringSysDictDataMap();
            SysDictData sysDictData = dictMap.get(type.toString());
            RealTimeDataDTO realTimeData = new RealTimeDataDTO();
            realTimeData.setDeviceId(deviceId);
            realTimeData.setDictValue(type);
            realTimeData.setDataUnit(sysDictData.getValueUnit());
            realTimeData.setDeviceType(DeviceTypeEnum.LIDAR.getCode());
            realTimeData.setDataValue(storage.get(i));
            realTimeDataList.add(realTimeData);

        }

    }

}
